/*
	Copyright (C) 2010 Salil Bhagurkar

	This file is part of salil64

	salil64 is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	salil64 is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with salil64.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <lib.h>
#include <kprint.h>
#include "../arch/asm.h"
#include <bitmap.h>

#define PAGE_SIZE 4096

/*In one page,
 * 4096 * 8 = 32768 pages can be bitmapped
 */

/*
 * Basic mapping that maps 4mb of pages
 * That is 1024 pages = 1024 / 8 = 128 bytes = 16 unsigned longs
 */


struct pageBitmap {
	void *address;
	unsigned long nrPages;
	struct pageBitmap *prev;
	struct pageBitmap *next;
	unsigned long *bitmap; //This should point to memory after the pageBitmap struct for bitmaps after the base one
};

#define PAGEBITMAP_NR ((PAGE_SIZE - sizeof(struct pageBitmap)) / 8)
#define PAGEBITMAP_NRPAGES (PAGEBITMAP_NR * 64)

static unsigned long baseBitmap[PAGEBITMAP_NR];

static struct pageBitmap basePageBitmap = {.address = NULL, .bitmap = baseBitmap, .nrPages = PAGEBITMAP_NRPAGES};


static void *pageAllocGetFreePage(struct pageBitmap *pgBmp)
{
	unsigned long i;
	for(i = 0; i < pgBmp->nrPages; i++) {
		if(!getBit(pgBmp->bitmap, i)) {
			setBit(pgBmp->bitmap, i);
			return pgBmp->address + i * PAGE_SIZE;
		}
	}
	return NULL;
}

static void pageAllocSetFreePage(struct pageBitmap *pgBmp, unsigned long nr)
{
	clrBit(pgBmp->bitmap, nr);
}

void *pageAllocGet()
{
	void *page;
	page = pageAllocGetFreePage(&basePageBitmap);
	return page;
}

void pageAllocSetFree(void *addr)
{
	unsigned long pageNr;
	pageNr = ((unsigned long)addr) / PAGE_SIZE;
	pageAllocSetFreePage(&basePageBitmap, pageNr);
}

static void showMap(struct pageBitmap *pgBmp, int showFence)
{
	unsigned long i;
	unsigned long useStart = 0;
	char used = 0;
	for(i = 0; i < pgBmp->nrPages; i++) {
		if(getBit(pgBmp->bitmap, i)) {
			if(showFence)
				kPrint("|");
			if(used == 0) {
				useStart = i;
				used = 1;
			}
		} else {
			if(showFence)
				kPrint("_");
			if(used == 1) {
				kPrint("Used address: " $p (void *)(useStart * PAGE_SIZE) $ " - " $p (void *)(i * PAGE_SIZE) $ "\n");
				used = 0;
			}
		}
	}
}

/*Setup the base bitmap*/
void pageAllocSetup()
{
	//Map the kernel pages and first 1 mb
	unsigned long page_kernelEnd;
	page_kernelEnd = _kernelEnd / PAGE_SIZE;
	unsigned long i;
	memset(basePageBitmap.bitmap, 0, PAGEBITMAP_NR * sizeof(unsigned long));

	for(i = 0; i < page_kernelEnd; i++) {
		setBit(basePageBitmap.bitmap, i);
	}

}
